package com.parent.Interface.log;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONUtil;
import com.parent.enums.RequestMethod;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author yan
 * @date 2023/4/12 0012 18:31
 */
@Aspect
@Slf4j
@Component
@Getter
public class SysLogAspect implements SysLogCommon {
    private final static JSONConfig jsonConfig = JSONConfig.create().setIgnoreNullValue(false);

    @Resource
    private Environment environment;
    @Value("${operate.log.user.type.name:user_id}")
    private String userTypeName;
    /**
     * 需要获取请求行的请求方法
     */
    private final static List<String> getParamsMethod = Stream.
            of(RequestMethod.GET.name(), RequestMethod.DELETE.name()).collect(Collectors.toList());
    /**
     * 需要获取请求体的请求方法
     */
    private final static List<String> getBodyMethod = Stream.
            of(RequestMethod.POST.name(), RequestMethod.PUT.name()).collect(Collectors.toList());

    public String getUserAgent(HttpServletRequest request) {
        String ua = request.getHeader("User-Agent");
        return ua != null ? ua : "";
    }

    public static <T extends Annotation> T getClassAnnotation(JoinPoint joinPoint, Class<T> annotationClass) {
        return ((MethodSignature) joinPoint.getSignature()).getMethod().getDeclaringClass().getAnnotation(annotationClass);
    }

    /**
     * 获取module
     *
     * @param joinPoint
     * @return
     */
    public String getModule(JoinPoint joinPoint) {
        Tag tag = getClassAnnotation(joinPoint, Tag.class);

        String module = "";
        if (tag != null) {
            if (StrUtil.isNotEmpty(tag.name())) {
                // 优先读取 @Tag 的 name 属性
                module = tag.name();
            } else if (ArrayUtil.isNotEmpty(tag.description())) {
                // 没有的话，读取 @API 的 description 属性
                module = tag.description();
            }
        }
        return module;
    }

    /**
     * 是否存在注解，如果存在就获取
     */
    @Override
    public SysLog getAnnotationLog(JoinPoint joinPoint) {
//        OperateLog operateLog = getOperateLog(null);
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
//        log.debug("syslog:id:{}", operateLog.getTraceId());
        if (!ObjectUtils.isEmpty(method)) {
            return method.getAnnotation(SysLog.class);
        }
        return null;
    }


    @Override
    @Pointcut(value = "@annotation(SysLog)")
    public void SysLog() {
    }

    @SneakyThrows
    @Override
    @Before(value = "SysLog()")
    public void doBefore(JoinPoint joinPoint) throws IOException {
        SysLogCommon.super.doBefore(joinPoint);
        SysLog sysLog = getAnnotationLog(joinPoint);
        //获取是否有注解
        if (sysLog == null) {
            return;
        }
        boolean flag = sysLog.flag();
        if (flag) {
            // 接收到请求，记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            //可能有空指针问题

            HttpServletRequest request = attributes.getRequest();
            // 记录下请求内容、
            String applicationName = environment.getProperty("spring.application.name", String.class);

            String method = request.getMethod();

            Map<String, String> paramMap = ServletUtil.getParamMap(request);
            log.debug("parse:{}", JSONUtil.parse(paramMap, jsonConfig));

            String title = sysLog.title();
            String remoteAddr = request.getRemoteAddr();
            StringBuffer requestURL = request.getRequestURL();

            Object[] pointArgs = joinPoint.getArgs();
            String args = Arrays.toString(pointArgs);
            args = CollUtil.isEmpty(paramMap) ? args : JSONUtil.parse(paramMap, jsonConfig).toString();

            String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
            String name = joinPoint.getSignature().getName();
            log.info(new StringBuffer()
                            .append("\n====================================请求内容====================================")
                            .append("\n请求服务名 : {}")
                            .append("\n请求描述 : {}")
                            .append("\n请求IP : {}")
                            .append("\n请求地址 : {}")
                            .append("\n请求方式 : {}")
                            .append("\n请求参数 : {}")
                            .append("\n请求类方法 : {}.{}")
                            .append("\n================================================================================")
                            .toString()
                    , applicationName, title, remoteAddr, requestURL.toString(), method, args, declaringTypeName, name);
        }
    }

    @SneakyThrows
    @Override
    @AfterReturning(returning = "reValue", pointcut = "SysLog()")
    public void doAfterReturning(JoinPoint joinPoint, Object reValue) {
        SysLogCommon.super.doAfterReturning(joinPoint, reValue);
        SysLog sysLog = getAnnotationLog(joinPoint);
        if (ObjectUtils.isEmpty(sysLog)) {
            return;
        }
        boolean result = sysLog.result();
        JSON parse = null;
        if (result) {
            // 处理完请求，返回内容
            parse = JSONUtil.parse(reValue, jsonConfig);
            log.info(new StringBuffer()
                    .append("\n====================================响应内容====================================")
                    .append("\n响应 : {}")
                    .append("\n================================================================================")
                    .toString(), parse);
        }

    }

}
